09-13 - Sequences

Let us confess to God those things that are wrong in our work:
That the presence of God at work is often overlooked;
That creative people are often subjected to long, boring and unrelenting routines;
That skills are undeveloped through lack of training;
That resources are wasted in shoddy work and the production of unwanted goods;
That the maximisation of profit often excludes concern for people;
That men and women are discriminated against because of age, race, gender, disability, lack of skill and length of employment;
That the poor stand so little chance against the power of the rich, and the world’s destitute are forgotten.

Lord, have mercy upon us. Forgive us our sins and help us to amend our lives. Amen.
(https://www.theologyofwork.org/work-in-worship)

Retrieval

Is the following code right? What’s wrong with it?

from math import sqrt

a = int(input())
b = int(input())
c = int(input())

x1 = ( -b + sqrt(b^2 - 4ac) ) / 2a
x2 = ( -b - sqrt(b^2 - 4ac) ) / 2a

You have a total of 345 minutes. Write a Python code snippet to convert this total into hours and minutes. Explain how the modulus operator is used in your code to achieve the conversion.

What is the correct order of arithmetic operators in Python, from highest to lowest precedence?

  1. Addition (+), Subtraction (-)
  2. Multiplication (*), Division (/), Modulo (%)
  3. Exponentiation (**)
  1. 1 > 2 > 3
  2. 3 > 2 > 1
  3. 2 > 1 > 3
  4. 3 > 1 > 2

Which of the following code snippets correctly calculates the area of a circle with a radius of 5 using the math module?

import math
area = math.pi * (5 ** 2)
import math
area = 2 * math.pi * 5
import math
area = math.sqrt(5) * math.pi
import math
area = math.pow(5, 2) * math.pi

Object types

  • Object types are the ways we have to represent information in our programming.
  • Python has some built-in types we have already seen, but now we will see more.

Some of them are:

  • Numeric types: int, float, complex
  • Boolean types (logic): bool (True and False)
  • Sequence types: list, tuple, str, range, bytes, and others
  • Mapping type: dict
  • Set types: set, frozenset

It is always useful to consult documentation on each to see what you can and can’t do with them.

Container types

  • Some of these stand for objects that are collection of other objects. These are called containers.
  • For example, you may have a variable pointing to a single number (a numeric type). But you can also a variable pointing to a collection of numbers, or strings, or even other collections of numbers.
  • Think like “drawers” or “boxes” in a bookshelf

Subscriptable types

  • Remember our metaphor: objects are buildings, variables are addresses
    • As we have types of houses (residential, commercial), we also have types of objects (integer, float, string)
  • How is an address when we have a condo or apartment building - i.e., multiple “houses” in the same address?
    • “1234 Smith Ave Apt 101
  • It is also possible to have containers whose contents can be accessed by some “complement”. These are called subscripts.
    • To access a value, we write the variable followed by some value inside square brackets []

For example, a Python list:

condo = ["room 1", "room 2", "room 3"]
print(condo[0])
room 1

Notice that if we try to “subscript” an object which is not subscriptable, we get an error:

house = 3
print(house[0])
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[2], line 2
      1 house = 3
----> 2 print(house[0])

TypeError: 'int' object is not subscriptable

Sequence vs mapping types

  • In sequence types such as lists, tuples and strings, subscripts are integer numbers, which are called indexes.
    • Attention: indexes always starts at zero!
    • index 0 is for 1st element, index 1 is for 2nd, and so on…
condo = "room 1", "room 2", "room 3"
print(condo[0])
print(condo[1])
print(condo[2])
room 1
room 2
room 3
  • If the index can’t be found, we will have an error:
condo = "room 1", "room 2", "room 3"
print(condo[4])
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
Cell In[4], line 2
      1 condo = "room 1", "room 2", "room 3"
----> 2 print(condo[4])

IndexError: tuple index out of range
  • In mapping types, however, subscripts can be any kind of object (given that it is an immutable object — e.g., lists are not allowed), which are called, in this case, keys.
  • This is the case of the dictionary type (dict):
band = {"vocals": "John Anderson", "guitar":"Steve Howe", "bass": "Chris Squire", "keyboard": "Rick Wakeman"}
print(band["vocals"])
print(band["guitar"])
John Anderson
Steve Howe

Type properties

  • Subscriptable types: containers whose objects can be accessed using square brackets [];
  • Ordered types: containers whose elements are ordered and thus can be accessed by an index (integer number);
  • Immutable types: objects whose elements cannot be changed;
  • Callable types: functions and classes (check, for example, type(print) or type(math.cos))
    • You “call” these objects to execute some code by typing the object followed by parenthesis (and eventually pass some information inside the parenthesis): print("hi")
    • See what happens if you try calling a non-callable object:
a = 1
a()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[6], line 2
      1 a = 1
----> 2 a()

TypeError: 'int' object is not callable

Testing some syntax…

a = 1
a[0]
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[7], line 2
      1 a = 1
----> 2 a[0]

TypeError: 'int' object is not subscriptable
a = 1
a(0)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[8], line 2
      1 a = 1
----> 2 a(0)

TypeError: 'int' object is not callable
a = 1
a{0}
  Cell In[9], line 2
    a{0}
     ^
SyntaxError: invalid syntax

Summarizing properties

Type Container Subscriptable Ordered (sequence) Mutable
Numbers and booleans No No No No
Lists Yes Yes (integers) Yes Yes
Tuples Yes Yes (integers) Yes No
Strings Yes (only characters) Yes (integers) Yes No
Dictionaries Yes Yes (immutable objects) No Yes
Sets Yes (only immutable objects) No No Yes

Sequences

Sequences are ordered and subscriptable with integers (the indexes). The methods below thus work for all of these types: lists, tuples and strings.

Operation Result
x in s True if an item of s is equal to x, else False
x not in s False if an item of s is equal to x, else True
s + t the concatenation of s and t
s * n or n * s equivalent to adding s to itself n times
s[i] ith item of s, origin 0
s[i:j] slice of s from i to j
s[i:j:k] slice of s from i to j with step k
len(s) length of s
min(s) smallest item of s
max(s) largest item of s
s.index(x[, i[, j]]) index of the first occurrence of x in s (at or after index i and before index j)
s.count(x) total number of occurrences of x in s

Unpacking sequences

All sequence types can also be unpacked in multiple variables. For example:

s = ["I", "am", "your", "father"]
a, b, c, d = s
print(a)
print(b)
I
am
s = "hi!"
ch1, ch2, ch3 = s
print(ch1, ch2, ch3)
h i !

But careful: you will get an error if you don’t match the length:

a = [1, 2, 3]
v1, v2, v3, v4 = a
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[12], line 2
      1 a = [1, 2, 3]
----> 2 v1, v2, v3, v4 = a

ValueError: not enough values to unpack (expected 4, got 3)

Lists

Mutable sequences, represented as values separated with commas and enclosed with square brackets [].

  • It is possible to initialize an empty list with x = []

What can go in?

  • Lists and tuples can be a collection of items of any type.
x = (1, 3.33333, "hello", True, 4)
  • You can even make tuples of tuples, lists of lists, lists of tuples…
    • For example: a 3x3 matrix - a 3-element list of 3-element lists
mat = [[1,2,3],
      [4,5,6],
      [7,8,9]]
print(mat[0][1]) # accessing value in row 0 and column 1
2

List methods

Operation Result
s.append(x) appends x to the end of the sequence (same as s[len(s):len(s)] = [x])
s.clear() removes all items from s (same as del s[:])
s.copy() creates a shallow copy of s (same as s[:])
s.extend(t) or s += t extends s with the contents of t (for the most part the same as s[len(s):len(s)] = t)
s *= n updates s with its contents repeated n times
s.insert(i, x) inserts x into s at the index given by i (same as s[i:i] = [x])
s.pop() or s.pop(i) retrieves the item at i and also removes it from s
s.remove(x) remove the first item from s where s[i] is equal to x
s.reverse() reverses the items of s in place

Changing versus copying

When dealing with mutable objects, it is very important to check if an operation is changing the object or making a copy of it.

For example:

a = [1,2,3]
b = a
a[1] = 5
print(b)
[1, 5, 3]
  • What happened here? Wasn’t b supposed to remain [1,2,3]?

  • a and b are pointing to the same object (the list [1,2,3]). If we change something in a, we change in b and vice-versa.

  • You can check that with the function id(), which finds an unique integer identifier for each object.

print(id(a))
print(id(b))
2298973480896
2298973480896

It is different if we make a copy of the object.

a = [1,2,3]
b = a.copy()
a[1] = 5
print(a)
print(b)
[1, 5, 3]
[1, 2, 3]
  • Look at the different ids: they are different objects, and thus are independent of each other.
print(id(a))
print(id(b))
2298973467072
2298973588800
  • This happens because lists are mutable objects (just as dictionaries, as we’ll see).
    • Mutable objects need to be copied. Immutable objects don’t.

Tuples

  • As every sequence type, it is ordered and subscriptable with integer indexes.
  • Immutable, represented just as values separated with commas, or separated with commas and enclosed with parentheses:
fruits = "banana", "apple", "orange"

or

fruits = ("banana", "apple", "orange")

Why immutables?

Why use tuples since they can’t change?

  • They uses less memory and demands less processing.
  • They don’t need to be copied when attributing to a new variable (see later).

What if I really want to change?

fruits[1] = "pineapple" # try changing "apple" to "pineapple"
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[21], line 1
----> 1 fruits[1] = "pineapple" # try changing "apple" to "pineapple"

TypeError: 'tuple' object does not support item assignment
  • To “change” a tuple, for example, you would basically need to either:

    1. Create a new tuple (with the changes you want), or
    2. Convert the tuple to a list, change the value you want (lists are mutable), and convert it back to a tuple.
      • Which would mean creating a new list object (from the tuple), and then creating a new tuple object (from the list)… which can be actually very inefficient and costly…

Strings

  • Are immutable, but unlike tuples, they only permit characters as its contents. Specified with "".
a = "Hello World"
print(a[1])
print(a[4:9])
e
o Wor

Important: since they can’t change, all the methods applied to immutable objects are making copies of them! For example:

a = "Hello"
b = a.upper()
print(b)
print(id(a))
print(id(b))
HELLO
2298968361216
2298968377984

String methods

Operation Description
capitalize() Converts the first character to upper case
casefold() Converts string into lower case
center() Returns a centered string
count() Returns the number of times a specified value occurs in a string
encode() Returns an encoded version of the string
endswith() Returns true if the string ends with the specified value
expandtabs() Sets the tab size of the string
find() Searches the string for a specified value and returns the position of where it was found
format() Formats specified values in a string
format_map() Formats specified values in a string
index() Searches the string for a specified value and returns the position of where it was found
isalnum() Returns True if all characters in the string are alphanumeric
isalpha() Returns True if all characters in the string are in the alphabet
isascii() Returns True if all characters in the string are ascii characters
isdecimal() Returns True if all characters in the string are decimals
isdigit() Returns True if all characters in the string are digits
isidentifier() Returns True if the string is an identifier
islower() Returns True if all characters in the string are lower case
isnumeric() Returns True if all characters in the string are numeric
isprintable() Returns True if all characters in the string are printable
isspace() Returns True if all characters in the string are whitespaces
istitle() Returns True if the string follows the rules of a title
isupper() Returns True if all characters in the string are upper case
join() Converts the elements of an iterable into a string
ljust() Returns a left justified version of the string
lower() Converts a string into lower case
lstrip() Returns a left trim version of the string
maketrans() Returns a translation table to be used in translations
partition() Returns a tuple where the string is parted into three parts
replace() Returns a string where a specified value is replaced with a specified value
rfind() Searches the string for a specified value and returns the last position of where it was found
rindex() Searches the string for a specified value and returns the last position of where it was found
rjust() Returns a right justified version of the string
rpartition() Returns a tuple where the string is parted into three parts
rsplit() Splits the string at the specified separator, and returns a list
rstrip() Returns a right trim version of the string
split() Splits the string at the specified separator, and returns a list
splitlines() Splits the string at line breaks and returns a list
startswith() Returns true if the string starts with the specified value
strip() Returns a trimmed version of the string
swapcase() Swaps cases, lower case becomes upper case and vice versa
title() Converts the first character of each word to upper case
translate() Returns a translated string
upper() Converts a string into upper case
zfill() Fills the string with a specified number of 0 values at the beginning

Values in representations

What data structure would you use to represent the following? Write down and discuss with your colleagues.

  • The first names of all the students in this course

  • An address book entry (name, email, major, …)

  • A person’s ethnicity

  • A coordinate point, e.g., (0, 0)

  • The atomic number of every element in the periodic table

  • A bag with different colored pieces to be drawn randomly? (For example, in a game like [The Quacks of Quedlinburg](https://boardgamegeek.com/boardgame/244521/quacks-quedlinburg)

  • Every representation is a selective portrait of a reality according to certain interests of who represents it. You can depict only certain parts, at certain moments, and with a certain structure.

  • Thus, we can ask: are we encoding reality correctly?

  • Remember our data types characteristics:

    • Numeric, boolean, containers…
    • Immutable / Mutable
    • Ordered / Non-ordered
    • Callable / Non-callable

Programmers are encoders

Everytime we represent some thing with a data structure, we are supposing that this thing is correctly portrayed with this structure.

  • For example, is a list of people’s name something we can “order”? And for what purpose are we doing that?

  • For example, can emotion be represented with a number? A boolean value? A dictionary indicating multiple aspects of an emotion?

Limits of data representations

Data can be:

  • imprecise (including: what it can mean for someone may not mean the same thing for another);

  • ambiguous (may mean multiple things depending on the context);

  • not comprehensive enough (or what we call biased - it is limited to some specific population or situation and thus is not generalizable);

  • distorted (“artifacts” - we cannot always be sure it is being transmitted or recorded faithfully);

  • or even not timely enough (things changed since we got them).

This happens because we limited, situated and always see things from our perspective. This is not bad, however, we need to acknowledge this limitedness. We are not possessors of an all-encompassing truth.

“Through the confusion of tongues, through non-communication, God prevents man from constructing [for himself] a truth valid for all men. In this way, man’s truth will always be partial and contestable” (Jacques Ellul, The Meaning of the City, p. 19).

Encoding and ruling with justice

  • Remember the origin of the word code: a corpus of law;

  • When we encode things, we are setting a rule;

  • Thus, we are reflecting our kingly vocation as human beings;

  • The Bible talks a lot about JUSTICE as the virtue of a king.

  • To encode correctly, we need to see correctly. And to see correctly, we need a fundamental orientation of heart towards God and his wisdom.

For the Lord grants wisdom! From his mouth come knowledge and understanding. He grants a treasure of common sense to the honest. He is a shield to those who walk with integrity. He guards the paths of the just and protects those who are faithful to him. Then you will understand what is right, just, and fair, and you will find the right way to go. Proverbs 2.6-10

  • We also need a broad knowledge base: “[In ICT design,] one needs to have as broad a knowledge base as possible. It is the outer parameters that one must have knowledge about.” (Jacob and Ebrahimpur 2001, 78)
    • Sociologist Harry Collins calls that interactional expertise - you are not an expert practitioner in the area, but you are informed enough to talk meaninfully with its practicioners.